package com.ifast.sys.filter.token;


import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.google.gson.Gson;
import com.ifast.common.csrftoken.CsrfToken;
import com.ifast.common.csrftoken.CsrfTokenCheckException;
import com.ifast.common.csrftoken.util.ObjectUtil;
import com.ifast.common.csrftoken.util.StringUtil;
import com.ifast.common.utils.Result;

import lombok.extern.slf4j.Slf4j;


/**
 * 项目名称： 防止csrf
 * 类名称：CsrfTokenInterceptor
 * 类描述：  csrftoken认证拦截器
 * 创建人：ShiQiang 
 * 创建时间：2017年7月17日 上午11:41:32
 * 修改人：Administrator 
 * 修改时间：2017年7月17日 上午11:41:32
 * 修改备注： 
 * @version
 */
@Slf4j
public class CsrfAnnotTokenInterceptor extends HandlerInterceptorAdapter {
	 
	private String tokenKey;
    private int    maxTokens;
    
    public String getTokenKey() {
        return tokenKey;
    }

    public void setTokenKey(String tokenKey) {
        this.tokenKey = StringUtil.trimToNull(tokenKey);
    }
    
    public int getMaxTokens() {
        return maxTokens;
    }

    public void setMaxTokens(int maxTokens) {
        this.maxTokens = maxTokens;
    } 
 
	//解析token 
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        
    	 
    	
    	CsrfTokenCheck methodCheck = null;
		try{
			if(handler instanceof HandlerMethod){
				methodCheck = ((HandlerMethod) handler).getMethodAnnotation(CsrfTokenCheck.class); 			   // 方法注解
			} 
		}catch (ClassCastException ex){
			log.info("转换异常",ex);
			return false;
		}
		//有     说明必须进行校验
		boolean checkflag = true;
		if (methodCheck != null) { 
			boolean reput = methodCheck.checkValue();
			boolean longPut = methodCheck.checkLongValue();
			boolean ajaxReput = methodCheck.checkAjaxValue();
			boolean ajaxLong = methodCheck.checkAjaxLongValue();  
			if(reput || longPut || ajaxReput || ajaxLong){
				checkflag = checkToken(reput,longPut,ajaxReput,ajaxLong,request,response);	
			}  
			//对ajax请求放置token
			boolean setAjaxReput = methodCheck.putAjax();
			boolean setAjaxLong = methodCheck.putAjaxLongValue();
			if(setAjaxReput || setAjaxLong){
				putToken(setAjaxReput,setAjaxLong,request,response);	
			}
		}  
		return checkflag;
    }  
    
    
    private boolean checkToken(boolean reput,boolean longPut,boolean ajaxReput,
    		boolean ajaxLong,HttpServletRequest request,HttpServletResponse response)throws Exception {
    	
    	String xrq = request.getHeader("X-Requested-With");
    	tokenKey = ObjectUtil.defaultIfNull(tokenKey, CsrfToken.DEFAULT_TOKEN_KEY);
    	
    	boolean checkflag = false;
    	if(StringUtils.isBlank(xrq)){
    		String tokenFromRequest = StringUtil.trimToNull(request.getParameter(tokenKey)); 
        	
    		if(StringUtils.isBlank(tokenFromRequest)){
    			sendError("normal",request,response);
                return false; 
        	} 
    		
    		HttpSession session = request.getSession();
    		if(reput){
    			List<String> tokensInSession = CsrfToken.getTokensInSession(session, tokenKey);
    			if (!tokensInSession.contains(tokenFromRequest)) { 
    				sendError("normal",request,response);
                    return false;   
                } else {
                    // 如果符合，则清除session中相应的token，以防止再次使用它
                    tokensInSession.remove(tokenFromRequest); 
                    CsrfToken.setTokensInSession(session, tokenKey, tokensInSession);
                    checkflag = true;
                }
    		}else if(longPut){
    			 if (!tokenFromRequest.equals(CsrfToken.getLongLiveTokenInSession(session))) {
    				 sendError("normal",request,response);
                     return false; 
    			 }else{
    				 checkflag = true; 
    			 }
    		} 
    	}else{ 
    		
    		String tokenFromRequest = StringUtil.trimToNull(request.getHeader(tokenKey));
            if(StringUtils.isBlank(tokenFromRequest)){
            	 sendError("ajax",request,response);  
                 return false;   
        	} 
    		HttpSession session = request.getSession();
    		if(ajaxReput){
    			List<String> tokensInSession = CsrfToken.getTokensInSession(session, tokenKey);
    			if (!tokensInSession.contains(tokenFromRequest)) { 
    				sendError("ajax",request,response);
                    return false;   
                } else {
                    // 如果符合，则清除session中相应的token，以防止再次使用它
                    tokensInSession.remove(tokenFromRequest); 
                    CsrfToken.setTokensInSession(session, tokenKey, tokensInSession);
                    checkflag = true;
                }
    		}else if(ajaxLong){
    			 if (!tokenFromRequest.equals(CsrfToken.getLongLiveTokenInSession(session))) {
    				 sendError("ajax",request,response);
                     return false; 
    			 }else{
    				 checkflag = true; 
    			 }
    		} 
           
    	}
    	try {
    		// 在thread上下文中保存当前的tokenKey，以便使其它csrfToken的检查都能使用统一的key。
    		CsrfToken.setContextTokenConfiguration(tokenKey, maxTokens);
    		return checkflag;
    	} finally {
           CsrfToken.resetContextTokenConfiguration();
    	} 
    }
    private void putToken(boolean reput,boolean longPut,HttpServletRequest request,HttpServletResponse response)throws Exception {
    	CsrfToken csrftoken = getToken(request); 
		if(reput){
    		response.addHeader("csrf", csrftoken.getUniqueToken()); 
    	}
    	if(longPut){  
    		response.setHeader("csrf"+"Long",csrftoken.getLongLiveToken()); 
    	}   
    }
    
    private void sendError(String type,HttpServletRequest request,HttpServletResponse response)throws Exception {
    	if("ajax".equals(type)){
    		 Map<String,String> map = new HashMap<String,String>();
         	 map.put("msg", "验证错误!");
        	 map.put("code", "400");
        	 response.setStatus(400);
             response.setContentType("application/json;charset=UTF-8");  
             PrintWriter out = response.getWriter();  
             out.print(new Gson().toJson(Result.ok(map)));  
             response.flushBuffer();
             
    	}else{
    		log.info("csrf验证：没有通过！");
    		throw new CsrfTokenCheckException("重复提交"); 
    	}
    }
    
    
    
    private CsrfToken getToken(HttpServletRequest request){
    	CsrfToken.setContextTokenConfiguration(tokenKey, maxTokens);
		CsrfToken csrftoken = new CsrfToken(request);
		return csrftoken;
    }
    
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception { 
    	
    	CsrfTokenCheck methodPut = null; 
		try{
			if(handler instanceof HandlerMethod){
				methodPut = ((HandlerMethod) handler).getMethodAnnotation(CsrfTokenCheck.class); 			   // 方法注解
			}
		}catch (ClassCastException ex){
			log.info("转换异常",ex); 
		} 
		//有     说明必须进行校验
		if (methodPut != null) {
			boolean reput = methodPut.putValue();
			boolean longPut = methodPut.putLongValue(); 
        	if(reput || longPut){ 
        		if(modelAndView != null){ 
	            	modelAndView.addObject("csrf",getToken(request)); 
		    	} 
        	} 
		} 
    }
    
    
    
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception { 
    }
}
